package com.zzyl.nursing.service.impl;

import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import cn.hutool.jwt.JWTUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.zzyl.common.utils.StringUtils;
import com.zzyl.nursing.domain.FamilyMember;
import com.zzyl.nursing.dto.MemberLoginDTO;
import com.zzyl.nursing.properties.WxMinProgramProperties;
import com.zzyl.nursing.service.IFamilyMemberService;
import com.zzyl.nursing.service.IMemberService;
import com.zzyl.nursing.vo.LoginVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * @author byZhao
 * @create 2025/7/2 11:58
 * @description
 */
@SuppressWarnings("all")
@Slf4j
@Service
public class MemberServiceImpl implements IMemberService {


    @Autowired
    private WxMinProgramProperties wxProp;
    @Autowired
    private IFamilyMemberService fmService;
    @Autowired
    private StringRedisTemplate redis;

    @Value("${jwt.secretKey}")
    private String jwtSecretKey;


    @Override
    public LoginVO login(MemberLoginDTO dto) {
        // 1. 根据code 调用微信小程序提供的官方登录接口
        JSONObject userInfoMap = this.getUserLoginInfoFromWX(dto.getCode());
        // 2. 获取openId
        String openid = userInfoMap.get("openid") + "";
        // 3. 根据openI的查询数据库总family_member
        FamilyMember familyMember = fmService.getOne(Wrappers.lambdaQuery(FamilyMember.class).eq(FamilyMember::getOpenId, openid));
        // 4. 判断用户是否为空，如果为空创建用户，并且赋值openId
        if (ObjectUtil.isEmpty(familyMember)) {
            familyMember = new FamilyMember();
            familyMember.setOpenId(openid);
        }
        // 5.根据手机号凭证 phoneCode 调用微信接口获取手机号
        JSONObject phoneMap = this.getPhoneNumberByPhoneCodeFormWX(dto.getPhoneCode(), openid);
        // 6. 判断依据：errorcode 不是0 失败了
        Object errorcode = phoneMap.get("errcode");
        int code = Integer.parseInt(errorcode.toString());
        if (code != 0) {
            throw new RuntimeException("登录信息有误！");
        }
        Map<String, Object> phoneInfo = (Map<String, Object>) phoneMap.get("phone_info");
        // 提取手机号
        String phoneNumber = phoneInfo.get("phoneNumber") + "";
        // 设置手机号
        familyMember.setPhone(phoneNumber);
        //  根据familyMember 如果有id 自动做修改，如果没有id做新增操作
        this.saveOrUpdateFamilyMember(familyMember,phoneNumber);

        // 基于用户的id 生成一个jwt令牌： 若依生成框架因此首先考虑若依是否提供，hutool工具包
        Map<String, Object> map = new HashMap<>();
        map.put("userId", familyMember.getId());
        map.put("nickName", dto.getNickName());
        // hutool工具包 生成令牌
        String token = JWTUtil.createToken(map, jwtSecretKey.getBytes());
        // 构建返回数据
        LoginVO loginVO = new LoginVO();
        loginVO.setToken(token);
        loginVO.setNickName(familyMember.getName());
        return loginVO;
    }

    static List<String> DEFAULT_NICKNAME_PREFIX = ListUtil.of(
            "生活更美好",
            "大桔大利",
            "日富一日",
            "好柿开花",
            "柿柿如意",
            "一椰暴富",
            "大柚所为",
            "杨梅吐气",
            "天生荔枝",
            "高富帅",
            "白富美",
            "傻白甜"
    );


    /**
     * 保存或修改客户
     *
     * @param member
     * @param phone
     */
    private void saveOrUpdateFamilyMember(FamilyMember member, String phone) {

        // 1.判断取到的手机号与数据库中保存的手机号不一样
        if (ObjectUtil.notEqual(phone, member.getPhone())) {
            // 修改手机号
            member.setPhone(phone);
        }
        // 2.判断id存在，则修改
        if (ObjectUtil.isNotEmpty(member.getId())) {
            fmService.updateById(member);
            return;
        }
        // 3.id不存在，保存新用户
        // 随机组装昵称，词组+手机号后四位
        String nickName = DEFAULT_NICKNAME_PREFIX.get((int) (Math.random() * DEFAULT_NICKNAME_PREFIX.size()))
                + StringUtils.substring(member.getPhone(), 7);

        member.setName(nickName);
        fmService.saveOrUpdate(member);
    }


    /**
     * 获取访问定牌接口地址
     */
    private static final String WX_ACCESS_TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token";

    /**
     * 获取访问令牌
     *
     * @return 访问令牌
     * https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/mp-access-token/getAccessToken.html
     */
    private String getAccessToken() {
        String url = WX_ACCESS_TOKEN_URL +
                "?grant_type=client_credential" +
                "&appid=" + wxProp.getAppId() +
                "&secret=" + wxProp.getAppSecret();
        String result = HttpUtil.get(url);
        String accessToken = JSONUtil.parseObj(result).getStr("access_token");
        return accessToken;
    }


    private static final String ACCESS_TOKEN_REDIS_KEY_PREFIX = "wx:access_token:";


    private static final String WX_GET_PHONE_NUMBER_URL = "https://api.weixin.qq.com/wxa/business/getuserphonenumber?access_token=";

    /***
     * 调用微信获取手机号接口获取手机号
     * @param phoneCode  手机号凭证
     * @return 手机号码
     *  https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/user-info/phone-number/getPhoneNumber.html
     *
     *  问题 获取访问令牌 access_token [5分钟之内都是有效的！]
     */
    private JSONObject getPhoneNumberByPhoneCodeFormWX(String phoneCode, String openId) {
        // 从redis中获取访问令牌，如果存在则直接使用，否则调用生成令牌方法
        String accessToken = null;
        try {
            String redisKey = ACCESS_TOKEN_REDIS_KEY_PREFIX + openId;
            accessToken = redis.opsForValue().get(redisKey);
        } catch (Exception e) {
            log.error("调用微信手机号获取接口中，从redis中获取访问令牌失败，失败原因是：{}", e.getMessage());
        }
        if (ObjectUtil.isEmpty(accessToken)) {
            // 重新调用微信的获取令牌的接口获取令牌
            accessToken = this.getAccessToken();
            // 存入redis
            try {
                // 经典bug
                redis.opsForValue().set(ACCESS_TOKEN_REDIS_KEY_PREFIX + openId, accessToken,240, TimeUnit.SECONDS);
            } catch (Exception e) {
                log.error("存入redis失败，失败的原因：{}", e.getMessage());
            }
        }
        // 调用根据手机号凭证获取手机号的接口
        String url = WX_GET_PHONE_NUMBER_URL + accessToken;
        Map<String, Object> param = new HashMap<>();
        param.put("code", phoneCode);
        String jsonStr = JSONUtil.toJsonStr(param);
        String post = HttpUtil.post(url, jsonStr);
        JSONObject jsonObject = JSONUtil.parseObj(post);
        return jsonObject;
    }

    /**
     * 定义一个微信小程序登录地址常量
     */
    private static final String WX_MP_LOGIN_URL = "https://api.weixin.qq.com/sns/jscode2session";

    /**
     * 调用第三方微信登录接口获取用户登录信息
     * https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/user-login/code2Session.html
     *
     * @param code 临时登录陆凭证
     * @return Map 结果
     */
    private JSONObject getUserLoginInfoFromWX(String code) {
        String url = WX_MP_LOGIN_URL + "?" +
                "appid=" + wxProp.getAppId() +
                "&secret=" + wxProp.getAppSecret() +
                "&js_code=" + code +
                "&grant_type=authorization_code";
        // 调用微信登录接口地址
        String jsonStr = HttpUtil.get(url);
        System.out.println(jsonStr);
        // 将string转成一个JSONObject
        return JSONUtil.parseObj(jsonStr);
    }


}
